/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on Oct 29, 2006
* @author Fabio
*/
package org.python.pydev.navigator.ui;
import java.io.File;
import java.net.URI;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IURIEditorInput;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.internal.navigator.ContributorTrackingSet;
import org.eclipse.ui.internal.navigator.NavigatorContentService;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.ui.navigator.CommonViewer;
import org.eclipse.ui.navigator.INavigatorPipelineService;
import org.eclipse.ui.navigator.PipelinedShapeModification;
import org.eclipse.ui.part.IShowInTarget;
import org.eclipse.ui.part.ShowInContext;
import org.python.pydev.core.ExtensionHelper;
import org.python.pydev.core.callbacks.CallbackWithListeners;
import org.python.pydev.core.callbacks.ICallbackWithListeners;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.core.structure.TreeNode;
import org.python.pydev.editorinput.PydevZipFileEditorInput;
import org.python.pydev.editorinput.PydevZipFileStorage;
import org.python.pydev.navigator.LabelAndImage;
import org.python.pydev.navigator.actions.PythonLinkHelper;
import org.python.pydev.navigator.elements.IWrappedResource;
import org.python.pydev.ui.IViewCreatedObserver;
import org.python.pydev.ui.IViewWithControls;
/**
* This class is the package explorer for pydev. It uses the CNF (Common Navigator Framework) to show
* the resources as python elements.
*/
@SuppressWarnings({ "restriction", "rawtypes", "unchecked" })
public class PydevPackageExplorer extends CommonNavigator implements IShowInTarget, IViewWithControls {
/**
* This viewer is the one used instead of the common viewer -- should only be used to fix failures in the base class.
*/
public static class PydevCommonViewer extends CommonViewer {
/**
* This is used so that we only restore the memento in the 'right' place
*/
public boolean availableToRestoreMemento = false;
/**
* This is the pydev package explorer
*/
private PydevPackageExplorer pydevPackageExplorer;
public PydevPackageExplorer getPydevPackageExplorer() {
return pydevPackageExplorer;
}
public PydevCommonViewer(String id, Composite parent, int style, PydevPackageExplorer pydevPackageExplorer) {
super(id, parent, style);
this.pydevPackageExplorer = pydevPackageExplorer;
//We need to be able to compare actual resources and IWrappedResources
//as if they were the same thing.
setComparer(new IElementComparer() {
public int hashCode(Object element) {
if (element instanceof IWrappedResource) {
IWrappedResource wrappedResource = (IWrappedResource) element;
return wrappedResource.getActualObject().hashCode();
}
return element.hashCode();
}
public boolean equals(Object a, Object b) {
if (a instanceof IWrappedResource) {
IWrappedResource wrappedResource = (IWrappedResource) a;
a = wrappedResource.getActualObject();
}
if (b instanceof IWrappedResource) {
IWrappedResource wrappedResource = (IWrappedResource) b;
b = wrappedResource.getActualObject();
}
if (a == null) {
if (b == null) {
return true;
} else {
return false;
}
}
if (b == null) {
return false;
}
return a.equals(b);
}
});
}
/**
* Returns the tree path for the given item.
*
* It's overridden because when using mylyn, the paths may be expanded but not shown, so segment is null
* -- that's why we return null if a given segment is null (instead of the assert that it contains in the superclass)
* @since 3.2
*/
@Override
protected TreePath getTreePathFromItem(Item item) {
LinkedList<Object> segments = new LinkedList<Object>();
while (item != null) {
Object segment = item.getData();
if (segment == null) {
return null;
}
segments.addFirst(segment);
item = getParentItem(item);
}
return new TreePath(segments.toArray());
}
}
/**
* This is the memento to be used.
*/
private IMemento memento;
public final ICallbackWithListeners onControlCreated = new CallbackWithListeners();
public final ICallbackWithListeners onControlDisposed = new CallbackWithListeners();
private PydevCommonViewer viewer;
private final PythonLinkHelper pythonLinkHelper = new PythonLinkHelper();
public PydevPackageExplorer() {
super();
List<IViewCreatedObserver> participants = ExtensionHelper
.getParticipants(ExtensionHelper.PYDEV_VIEW_CREATED_OBSERVER);
for (IViewCreatedObserver iViewCreatedObserver : participants) {
iViewCreatedObserver.notifyViewCreated(this);
}
}
/**
* Overridden to keep the memento to be used later (it's private in the superclass).
*/
public void init(IViewSite aSite, IMemento aMemento) throws PartInitException {
super.init(aSite, aMemento);
memento = aMemento;
}
/**
* Overridden to create our viewer and not the superclass CommonViewer.
*
* (Unfortunately, the superclass does a little more than creating it, so, we have to do those operations here
* too -- that's why we have to keep the memento object in the init method).
*/
@Override
protected CommonViewer createCommonViewer(Composite aParent) {
//super.createCommonViewer(aParent); -- don't even call the super class
CommonViewer aViewer = new PydevCommonViewer(getViewSite().getId(), aParent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL, this);
initListeners(aViewer);
//commented: we do that only after the part is completely created (because otherwise the state is reverted later)
//aViewer.getNavigatorContentService().restoreState(memento);
return aViewer;
}
/**
* Overridden because if the state is not restored as the last thing, it is reverted back to the previous state.
*/
@Override
public void createPartControl(Composite aParent) {
super.createPartControl(aParent);
PydevCommonViewer viewer = (PydevCommonViewer) getCommonViewer();
this.viewer = viewer;
onControlCreated.call(viewer);
viewer.availableToRestoreMemento = true;
for (int i = 0; i < 3; i++) {
try {
//I don't know why the 1st time we restore it it doesn't work... so, we have to do it twice
//(and the other 1 is because we may have an exception in the 1st step).
viewer.getNavigatorContentService().restoreState(memento);
} catch (Exception e1) {
if (i > 1) {
Log.log("Unable to restore the state of the Pydev Package Explorer.", e1);
}
}
}
}
public void dispose() {
if (viewer != null) {
onControlDisposed.call(viewer);
viewer = null;
}
super.dispose();
};
/**
* Returns the element contained in the EditorInput
*/
Object getElementOfInput(IEditorInput input) {
if (input instanceof IFileEditorInput) {
return ((IFileEditorInput) input).getFile();
}
if (input instanceof IURIEditorInput) {
IURIEditorInput iuriEditorInput = (IURIEditorInput) input;
URI uri = iuriEditorInput.getURI();
return new File(uri);
}
if (input instanceof PydevZipFileEditorInput) {
PydevZipFileEditorInput pydevZipFileEditorInput = (PydevZipFileEditorInput) input;
try {
IStorage storage = pydevZipFileEditorInput.getStorage();
if (storage instanceof PydevZipFileStorage) {
PydevZipFileStorage pydevZipFileStorage = (PydevZipFileStorage) storage;
return pydevZipFileStorage;
}
} catch (CoreException e) {
Log.log(e);
}
}
return null;
}
/**
* Implements the 'show in...' action
*/
public boolean show(ShowInContext context) {
Object elementOfInput = null;
ISelection selection = context.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = ((IStructuredSelection) selection);
if (structuredSelection.size() == 1) {
elementOfInput = structuredSelection.getFirstElement();
}
}
Object input = context.getInput();
if (input instanceof IEditorInput) {
elementOfInput = getElementOfInput((IEditorInput) context.getInput());
}
return elementOfInput != null && tryToReveal(elementOfInput);
}
/**
* This is the method that actually tries to reveal some item in the tree.
*
* It will go through the pipeline to see if the actual object to reveal has been replaced in the replace pipeline.
*/
public boolean tryToReveal(Object element) {
element = getPythonModelElement(element);
if (element instanceof PydevZipFileStorage) {
pythonLinkHelper.setCommonViewer(this.getCommonViewer());
PydevZipFileStorage pydevZipFileStorage = (PydevZipFileStorage) element;
IStructuredSelection externalFileSelectionInTree = pythonLinkHelper
.findExternalFileSelection((File) pydevZipFileStorage.zipFile);
if (externalFileSelectionInTree != null && !externalFileSelectionInTree.isEmpty()) {
Object firstElement = externalFileSelectionInTree.getFirstElement();
if (firstElement instanceof TreeNode) {
TreeNode treeNode = (TreeNode) firstElement;
//Ok, got to the zip file, let's try to find the path below it...
String zipPath = pydevZipFileStorage.zipPath;
List<String> split = StringUtils.split(zipPath, '/');
for (String string : split) {
List<TreeNode> children = treeNode.getChildren();
for (TreeNode<LabelAndImage> child : children) {
if (string.equals(child.getData().label)) {
treeNode = child;
break; //Goes on to the next substring...
}
}
}
if (revealAndVerify(new StructuredSelection(treeNode))) {
return true;
}
} else {
Log.log("Expected a TreeNode. Found: " + firstElement);
//Just go on to show the zip, not the internal contents...
if (revealAndVerify(externalFileSelectionInTree)) {
return true;
}
}
}
} else if (element instanceof File) {
pythonLinkHelper.setCommonViewer(this.getCommonViewer());
IStructuredSelection externalFileSelectionInTree = pythonLinkHelper
.findExternalFileSelection((File) element);
if (externalFileSelectionInTree != null && !externalFileSelectionInTree.isEmpty()) {
if (revealAndVerify(externalFileSelectionInTree)) {
return true;
}
}
}
//null is checked in the revealAndVerify function
if (revealAndVerify(element)) {
return true;
}
//if it is a wrapped resource that we couldn't show, try to reveal as a resource...
if (element instanceof IAdaptable && !(element instanceof IResource)) {
IAdaptable adaptable = (IAdaptable) element;
IResource resource = (IResource) adaptable.getAdapter(IResource.class);
if (resource != null) {
if (revealAndVerify(resource)) {
return true;
}
}
}
return false;
}
/**
* @param element the element that should be gotten as an element from the pydev model
* @return a pydev element or the same element passed as a parameter.
*/
private Object getPythonModelElement(Object element) {
if (element instanceof IWrappedResource) {
return element;
}
INavigatorPipelineService pipelineService = this.getNavigatorContentService().getPipelineService();
if (element instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) element;
IFile file = (IFile) adaptable.getAdapter(IFile.class);
if (file != null) {
HashSet<Object> files = new ContributorTrackingSet(
(NavigatorContentService) this.getNavigatorContentService());
files.add(file);
pipelineService.interceptAdd(new PipelinedShapeModification(file.getParent(), files));
if (files.size() > 0) {
element = files.iterator().next();
}
}
}
return element;
}
/**
* Tries to reveal some selection
* @return if it revealed the selection correctly (and false otherwise)
*/
private boolean revealAndVerify(Object element) {
if (element == null) {
return false;
}
if (element instanceof ISelection) {
selectReveal((ISelection) element);
} else {
selectReveal(new StructuredSelection(element));
}
return !getSite().getSelectionProvider().getSelection().isEmpty();
}
public ICallbackWithListeners getOnControlCreated() {
return onControlCreated;
}
public ICallbackWithListeners getOnControlDisposed() {
return onControlDisposed;
}
}